package com.libleveldb.util

import com.libleveldb.LevelDB
import com.libleveldb.WriteBatch
import com.libleveldb.exception.LevelDBException
import com.libleveldb.exception.LevelDBNotFoundException
import java.lang.ref.WeakReference
import java.util.*

/**
 * A simple implementation of [com.libleveldb.WriteBatch].
 */
class SimpleWriteBatch @JvmOverloads constructor(levelDB: LevelDB? = null) : WriteBatch {
    /**
     * A simple implementation of [com.libleveldb.WriteBatch.Operation].
     */
    private class Operation private constructor(
        private val type: Int,
        private val key: ByteArray,
        private val value: ByteArray?
    ) : WriteBatch.Operation {
        override fun key(): ByteArray{
            return key
        }

        override fun value(): ByteArray? {
            return value
        }

        override val isPut: Boolean
            get() = type == PUT
        override val isDel: Boolean
            get() = type == DELETE

        companion object {
            const val PUT = 0
            const val DELETE = 1
            fun put(key: ByteArray, value: ByteArray?): Operation {
                return Operation(PUT, key, value)
            }

            fun del(key: ByteArray): Operation {
                return Operation(DELETE, key, null)
            }
        }
    }

    /**
     * A [java.lang.ref.WeakReference] is used to allow for the easy and timely garbage collection of the [ ] instance by the GC, in case a WriteBatch reference wanders off.
     */
    private val levelDBWR: WeakReference<LevelDB?>?
    private val operations: LinkedList<WriteBatch.Operation>

    /**
     * {@inheritDoc}
     */
    override fun put(key: ByteArray?, value: ByteArray?): SimpleWriteBatch {
        if (value == null) {
            return del(key)
        }
        requireNotNull(key) { "Key must not be null." }
        operations.add(Operation.put(key, value))
        return this
    }

    /**
     * {@inheritDoc}
     */
    override fun del(key: ByteArray?): SimpleWriteBatch {
        requireNotNull(key) { "Key must not be null." }
        operations.add(Operation.del(key))
        return this
    }

    /**
     * {@inheritDoc}
     */
    override fun insert(operation: WriteBatch.Operation?): SimpleWriteBatch {
        requireNotNull(operation) { "Operation must not be null." }
        operations.add(operation)
        return this
    }

    /**
     * {@inheritDoc}
     */
    override fun iterator(): Iterator<WriteBatch.Operation?> {
        return operations.iterator()
    }

    /**
     * {@inheritDoc}
     */
    override val allOperations: Collection<WriteBatch.Operation>
        get() = ArrayList(operations)

    /**
     * {@inheritDoc}
     */
    @Throws(LevelDBException::class)
    override fun write(levelDB: LevelDB?, sync: Boolean) {
        levelDB!!.write(this, sync)
    }

    /**
     * {@inheritDoc}
     */
    @Throws(LevelDBException::class)
    override fun write(levelDB: LevelDB?) {
        write(levelDB, false)
    }

    /**
     * Like [.write] but asynchronously.
     *
     * @throws LevelDBException
     * @see .write
     */
    @JvmOverloads
    @Throws(LevelDBException::class)
    fun write(sync: Boolean = false) {
        val db = levelDBWR!!.get()
            ?: throw LevelDBNotFoundException("The LevelDB object is not reachable.")
        write(db, sync)
    }

    /**
     * Whether this [SimpleWriteBatch] object is bound to its creating [ ] instance.
     *
     * You cannot use [.write] or variants on a non-bound instance since the database object does not
     * exist.
     *
     * @return
     * @see .write
     * @see com.libleveldb.LevelDB.write
     */
    val isBound: Boolean
        get() = levelDBWR?.get() != null

    /**
     * Creates a new empty SimpleWriteBatch.
     *
     * Use [.LevelDB.write] or [SimpleWriteBatch.write] to write it to the database.
     */
    init {
        levelDBWR = WeakReference(levelDB)
        operations = LinkedList()
    }
}